home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
LispExample
/
LispText.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
12KB
|
485 lines
/*
** LispText.m
** A Text object to drive the Lisp object.
** Lee Boynton, NeXT, Inc., 1989
*/
#import <stdlib.h>
#import <string.h>
#import <sys/time.h>
#import <appkit/Window.h>
#import <appkit/publicWraps.h>
#import "Lisp.h"
#import "LispText.h"
@implementation LispText
#define DEFAULT_HISTORY 64
#define CLOSE_PAREN (')')
#define OPEN_PAREN ('(')
#define NEW_LINE ('\n')
#define CTRL_A (1)
#define CTRL_B (2)
#define CTRL_C (3)
#define CTRL_D (4)
#define CTRL_E (5)
#define CTRL_F (6)
#define CTRL_K (11)
#define CTRL_N (14)
#define CTRL_P (16)
unsigned short lispFilter(unsigned short theChar, int flags,
unsigned short charSet)
{
if (flags & NX_COMMANDMASK)
theChar = 0;
else if (theChar == NX_DELETE)
theChar = NX_BACKSPACE;
else if (theChar == NX_CR || theChar == NEW_LINE)
theChar = 0;
else if (theChar == '\t')
theChar = '\t';
else if (theChar < ' ')
theChar = 0;
return theChar;
}
static void initLispCategoryTable(id self)
{
static unsigned char lispTable[256];
unsigned char punctuationCategory, normalCategory;
unsigned char nullCategory, whiteSpaceCategory;
int i;
bcopy([self charCategoryTable],lispTable,sizeof(lispTable));
nullCategory = lispTable[0];
punctuationCategory = lispTable[';'];
normalCategory = lispTable['a'];
whiteSpaceCategory = lispTable[' '];
lispTable['!'] = normalCategory;
lispTable['&'] = normalCategory;
lispTable['('] = punctuationCategory;
lispTable[')'] = punctuationCategory;
lispTable['*'] = normalCategory;
lispTable['+'] = normalCategory;
lispTable['-'] = normalCategory;
lispTable['.'] = normalCategory;
lispTable['/'] = normalCategory;
lispTable[':'] = normalCategory;
lispTable['<'] = normalCategory;
lispTable['='] = normalCategory;
lispTable['>'] = normalCategory;
lispTable['?'] = normalCategory;
lispTable['@'] = normalCategory;
lispTable['['] = punctuationCategory;
lispTable['\\'] = normalCategory;
lispTable[']'] = punctuationCategory;
lispTable['{'] = punctuationCategory;
lispTable['|'] = normalCategory;
lispTable['}'] = punctuationCategory;
for(i=128;i<256;i++) lispTable[i] = nullCategory;
[self setCharCategoryTable:lispTable];
}
+ newFrame:(NXRect *)frameRect text:(char *)theText alignment:(int)align
{
int i;
self = [super newFrame:frameRect text:theText alignment:align];
[self setCharFilter:lispFilter];
[self setOpaque:YES];
[self setOverstrikeDiacriticals:NO];
initLispCategoryTable(self);
inputPosition = 0;
maxHistory = DEFAULT_HISTORY;
history = (char **)malloc(maxHistory*sizeof(char*));
for (i=0; i<maxHistory; i++) history[i] = NULL;
historyHead = 0;
connectedToLisp=NO;
theLisp = [Lisp new];
[theLisp setDelegate:self];
return self;
}
- free
{
[theLisp free];
return [super free];
}
- lispOutput:(const char *)theString
{
int offset;
NXSelPt selStart, selEnd;
if (!theString) return self;
offset = strlen(theString);
if (!connectedToLisp) {
int i = [self textLength];
[self setSel:0 :i];
[self replaceSel:theString];
inputPosition = offset;
[window makeFirstResponder:self];
connectedToLisp=YES;
} else {
[self getSel:&selStart :&selEnd];
[self setSel:inputPosition :inputPosition];
[self replaceSel:theString];
inputPosition += offset;
[self setSel:selStart.cp+offset :selEnd.cp+offset];
}
[self scrollSelToVisible];
return self;
}
/*
** NXBGetc - a text stream macro to get the previous character.
*/
typedef struct {
id text;
NXTextBlock *block;
} textInfo;
static char getPrevious(NXStream *s)
{
textInfo *info = (textInfo *) s->info;
NXTextBlock *block = info->block->prior;
if (!block) {
return EOF;
}
s->buf_base = block->text;
s->buf_ptr = s->buf_base + block->chars;
s->offset -= block->chars;
info->block = block;
return *(--s->buf_ptr);
}
#define NXBGetc(s) \
(((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \
*(--((s)->buf_ptr)) )
int findMatchingOpenParen(id text, char thisChar, char thatChar, int curPos)
{
int count = 1;
char ch;
NXStream *s = [text stream];
NXSeek(s, curPos, NX_FROMSTART);
while ((ch = NXBGetc(s)) != EOF) {
if (ch == thatChar && !(--count))
return NXTell(s);
else if (ch == thisChar)
count++;
}
return -1;
}
int findMatchingCloseParen(id text, char thisChar, char thatChar, int curPos)
{
int count = 1;
char ch;
NXStream *s = [text stream];
NXSeek(s, curPos, NX_FROMSTART);
while ((ch = NXGetc(s)) != EOF) {
if (ch == thatChar && !(--count))
return NXTell(s);
else if (ch == thisChar)
count++;
}
return -1;
}
void highlightChar(id text, int charPosition)
{
NXSelPt selStart, selEnd;
[text getSel:&selStart :&selEnd];
[text setSel:charPosition :charPosition+1];
[[text window] flushWindow];
NXPing();
[[text window] disableFlushWindow];
[text setSel:selStart.cp :selEnd.cp];
[[text window] reenableFlushWindow];
}
int searchBackwardsForChar(id text, char theChar, int curPos)
{
char ch;
NXStream *s = [text stream];
NXSeek(s, curPos, NX_FROMSTART);
while ((ch = NXBGetc(s)) != EOF) {
if (ch == theChar)
return NXTell(s);
}
return -1;
}
void insertChar(id self, char c, int pos)
{
char buf[2];
buf[0] = c;
buf[1] = 0;
[self setSel:pos :pos];
[self replaceSel:buf];
[self scrollSelToVisible];
}
static void addToHistory(id self, char *buf)
{
int i = (self->historyHead++) % self->maxHistory;
if (self->history[i])
free(self->history[i]);
self->history[i] = buf;
}
static char *getHistory(id self, int index)
{
int i = index % self->maxHistory;
return self->history[i];
}
- keyDown:(NXEvent *)theEvent
{
static int history_index=0;
int i, lastPos = [self textLength];
unsigned short val;
NXSelPt selStart, selEnd;
if (!connectedToLisp) {
NXBeep();
return self;
}
[self getSel:&selStart :&selEnd];
val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8);
switch (val) {
case CLOSE_PAREN:
if (selStart.cp < inputPosition)
[self setSel:lastPos :lastPos];
i = findMatchingOpenParen(self,CLOSE_PAREN,OPEN_PAREN,selEnd.cp);
[super keyDown:theEvent];
if (i >= 0)
highlightChar(self,i);
return self;
case NX_DELETE:
case NX_BACKSPACE:
if (selEnd.cp == selStart.cp) {
if (lastPos <= inputPosition || selStart.cp <= inputPosition) {
NXBeep();
return self;
}
} else {
if (selEnd.cp <= inputPosition ||
selStart.cp <= inputPosition) {
NXBeep();
return self;
}
}
break;
case NX_CR:
case NEW_LINE:
if (selEnd.cp >= inputPosition) {
char *buf;
i = lastPos - inputPosition;
buf = (char *)malloc(i+2);
if (i)
[self getSubstring:buf start:inputPosition length:i];
buf[i] = NEW_LINE;
buf[i+1] = 0;
inputPosition = lastPos+1;
[theLisp inputLisp:buf];
if (i) {
buf[i] = 0;
addToHistory(self,buf);
history_index = historyHead;
}
[self setSel:lastPos :lastPos];
insertChar(self,NEW_LINE,lastPos);
return self;
} else {
int len = selEnd.cp-selStart.cp;
if (len) {
char *buf = (char *)malloc(len+1);
[self getSubstring:buf start:selStart.cp length:len];
buf[len] = 0;
[self setSel:lastPos :lastPos];
[self replaceSel:buf];
free(buf);
[self scrollSelToVisible];
} else
[self setSel:lastPos :lastPos];
return self;
}
case CTRL_C:
[self setSel:lastPos :lastPos];
[theLisp interruptLisp];
return self;
break;
case CTRL_A:
[self setSel:inputPosition :inputPosition];
return self;
case CTRL_E:
[self setSel:lastPos :lastPos];
return self;
case CTRL_B:
if (selStart.cp < inputPosition)
[self setSel:inputPosition :inputPosition];
else if (selStart.cp != selEnd.cp)
[self setSel:selStart.cp :selStart.cp];
else if (selStart.cp > inputPosition)
[self setSel:selStart.cp-1 :selStart.cp-1];
else
NXBeep();
return self;
case CTRL_F:
if (selEnd.cp < inputPosition)
[self setSel:inputPosition :inputPosition];
else if (selStart.cp != selEnd.cp)
[self setSel:selEnd.cp :selEnd.cp];
else if (selEnd.cp < lastPos)
[self setSel:selEnd.cp+1 :selEnd.cp+1];
else
NXBeep();
return self;
case CTRL_D:
if (selStart.cp >= inputPosition) {
if (selStart.cp == selEnd.cp)
if (selEnd.cp >= lastPos) {
NXBeep();
return;
} else
[self setSel:selEnd.cp :selEnd.cp+1];
[self replaceSel:""];
} else
NXBeep();
return self;
case CTRL_K:
if (selStart.cp >= inputPosition) {
if (inputPosition != lastPos)
[self setSel:selStart.cp :lastPos];
[self replaceSel:""];
} else
NXBeep();
return self;
case CTRL_P: {
char *s;
if (!historyHead ||
(history_index <= (historyHead-self->maxHistory)))
return self;
s = getHistory(self, history_index-1);
if (!s) return self;
history_index--;
[window disableFlushWindow];
[self setSel:inputPosition :lastPos];
[self replaceSel:s];
[self scrollSelToVisible];
[window reenableFlushWindow];
[window flushWindow];
return self; }
case CTRL_N:
{
char *s;
if (!historyHead || (history_index >= (historyHead)))
return self;
s = getHistory(self, history_index);
if (s)
s = getHistory(self, ++history_index);
[window disableFlushWindow];
[self setSel:inputPosition :lastPos];
[self replaceSel:s];
[self scrollSelToVisible];
[window reenableFlushWindow];
[window flushWindow];
return self; }
default:
if (selStart.cp < inputPosition)
[self setSel:lastPos :lastPos];
break;
}
return [super keyDown:theEvent];
}
- clear:sender
{
NXSelPt selStart, selEnd;
[self getSel:&selStart :&selEnd];
if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
return self;
else
return [super clear:sender];
}
- cut:sender
{
NXSelPt selStart, selEnd;
[self getSel:&selStart :&selEnd];
if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
return self;
else
return [super cut:sender];
}
- paste:sender
{
int size = [self textLength];
NXSelPt selStart, selEnd;
[self getSel:&selStart :&selEnd];
if (selEnd.cp < inputPosition || selStart.cp < inputPosition)
[self setSel:size :size];
return [super paste:sender];
}
- mouseDown: (NXEvent *) theEvent
{
BOOL doubleClick;
doubleClick = (theEvent->data.mouse.click == 2);
[super mouseDown:theEvent];
if (theEvent->data.mouse.click == 2) {
char theChar;
int i;
NXSelPt selStart, selEnd;
[self getSel:&selStart :&selEnd];
[self getSubstring:&theChar start:selStart.cp length:1];
if (theChar == OPEN_PAREN) {
i = findMatchingCloseParen(self,theChar,CLOSE_PAREN,selEnd.cp+1);
if (i >= 0)
[self setSel:selStart.cp :i];
} else if (theChar == CLOSE_PAREN) {
i = findMatchingOpenParen(self,theChar,OPEN_PAREN,selEnd.cp-1);
if (i >= 0)
[self setSel:i :selEnd.cp];
}
}
return self;
}
- clearAll:sender
{
[self setSel:0 :[self textLength]];
[super clear:self];
inputPosition = 0;
return self;
}
- inputLisp:(const char *)theString
{
return [theLisp inputLisp:theString];
}
- interruptLisp
{
return [theLisp interruptLisp];
}
@end